package gspringboot

import (
	"gitee.com/hongzhaomin/gspring/iface"
	"gitee.com/hongzhaomin/gspring/tools"
	"gitee.com/hongzhaomin/hzm-common-go/assert"
	"sync"
	"time"
)

func NewGspringApplicationShutdownHook() *GspringApplicationShutdownHook {
	return &GspringApplicationShutdownHook{
		mu: new(sync.Mutex),
	}
}

type GspringApplicationShutdownHook struct {
	mu               *sync.Mutex
	logger           iface.GspringLogger
	contexts         []iface.ApplicationContext
	shutdownHandlers []func()
	inProgress       bool
}

func (my *GspringApplicationShutdownHook) registerApplicationContext(context iface.ApplicationContext) {
	my.assertNotInProgress()
	my.mu.Lock()
	defer my.mu.Unlock()
	my.contexts = append(my.contexts, context)
}

func (my *GspringApplicationShutdownHook) addShowdownHandler(handler func()) {
	my.assertNotInProgress()
	my.mu.Lock()
	defer my.mu.Unlock()
	my.shutdownHandlers = append(my.shutdownHandlers, handler)
}

func (my *GspringApplicationShutdownHook) assertNotInProgress() {
	assert.IsFalse(my.inProgress, "Shutdown in progress")
}

func (my *GspringApplicationShutdownHook) run() {
	my.assertNotInProgress()
	my.mu.Lock()
	defer my.mu.Unlock()
	my.inProgress = true
	for _, handler := range my.shutdownHandlers {
		handler()
	}
	for _, context := range my.contexts {
		my.closeAndWait(context)
	}
}

func (my *GspringApplicationShutdownHook) closeAndWait(context iface.ApplicationContext) {
	if !context.IsActive() {
		return
	}

	if err := context.Close(); err != nil {
		my.logger.Error("Application context closed error: %v", err)
	}

	sleep := time.Millisecond * 50
	timeout := time.Minute * 10
	waited := time.Duration(0)
	for context.IsActive() {
		if waited > timeout {
			my.logger.Warn("Timed out waiting for application context [%s] to become inactive",
				tools.GetFullPathName(context))
			return
		}
		time.Sleep(sleep)
		waited += sleep
	}
}
